#include "StdAfx.h"
#include "SimpleWaitableTimer.h"
#include "WindowsUtility.h"

namespace RoomFW {

SimpleWaitableTimer::SimpleWaitableTimer(void) :
	handle_()
{
	// "Safe" constructor.
}

SimpleWaitableTimer::SimpleWaitableTimer(const SimpleWaitableTimer& rhs) :
	handle_(),
	cs_()
{
	// Don't copy critical section!
	CriticalSectionGuard g(cs_);
	handle_ = rhs.handle_;
}

SimpleWaitableTimer::~SimpleWaitableTimer(void)
{
	{CriticalSectionGuard g(cs_);
		handle_.reset();
	}
}

SimpleWaitableTimer& SimpleWaitableTimer::operator=(const SimpleWaitableTimer& rhs)
{
	if (&rhs != this)
	{
		// Don't copy critical section!
		{CriticalSectionGuard g(cs_);
			handle_ = rhs.handle_;
		}
	}
	return *this;
}

SimpleWaitableTimer::operator HANDLE() const
{
	HANDLE retVal = 0;
	{CriticalSectionGuard g(cs_);
		retVal = handle_.get();
	}
	return retVal;
}

bool SimpleWaitableTimer::create(bool manualReset)
{
	bool retVal = false;
	{CriticalSectionGuard g(cs_);
		handle_.reset(::CreateWaitableTimer(0, // No security attributes.
											manualReset,
											0), // No name.
					  TimerHandleDeleter());
		SHOW_LAST_ERROR((0 != handle_), "SimpleWaitableTimer::create", "::CreateWaitableTimer");
		retVal = (0 != handle_);
	}
	return retVal;
}

bool SimpleWaitableTimer::set(const TimeValue& expireIn, const TimeValue& repeatEvery )
{
	// Set the first expiration.  Unit is 100 nanoseconds.  Negative indicates relative time.
	LARGE_INTEGER dueTime;
	LARGE_INTEGER ns;
	ns.QuadPart = 10000000;
	dueTime.QuadPart = -1 * (ns.QuadPart * expireIn.seconds() + expireIn.nanoSeconds() / 100);
	return this->set(&dueTime, repeatEvery.milliseconds());
}

bool SimpleWaitableTimer::set(const LARGE_INTEGER* dueTime, LONG period)
{
	BOOL ret = ::SetWaitableTimer(handle_.get(), dueTime, period, 0, 0, FALSE);
	SHOW_LAST_ERROR(ret, "SimpleWaitableTimer::set", "::SetWaitableTimer");
	return (ret ? true : false);
}

void SimpleWaitableTimer::cancel(void)
{
	BOOL ret = ::CancelWaitableTimer(handle_.get());
	SHOW_LAST_ERROR(ret, "SimpleWaitableTimer::cancel", "::CancelWaitableTimer");
}

bool SimpleWaitableTimer::wait(DWORD timeout)
{
	// Don't put blocking call in critical section.
	HANDLE h = 0;
	{CriticalSectionGuard g(cs_);
		h = handle_.get();
	}
	DWORD waitResult = ::WaitForSingleObject(h, timeout); 
	SHOW_LAST_ERROR((WAIT_FAILED != waitResult), "SimpleWaitableTimer::wait", "::WaitForSingleObject");
	return (WAIT_OBJECT_0 == waitResult);
}

void SimpleWaitableTimer::TimerHandleDeleter::operator()(HANDLE handle)
{
	BOOL ret = ::CancelWaitableTimer(handle);
	SHOW_LAST_ERROR(ret, "TimerHandleDeleter::operator()", "::CancelWaitableTimer");
	SharedHandleDeleter()(handle);
};

}
